#pragma once
#include "PDTExp.h"
#include "PDTVar.h"

class PDTUnion: public PDTExpSet {
	PDTUnion(PDTCodeGraph & CG, PDTExpSet & left, PDTExpSet & right) :
		PDTExpSet(CG) {
		addChild(&left);
		addChild(&right);

		left.addParent(this);
		right.addParent(this);
	}
	PDTExpSet * getLeft() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}
	PDTExpSet * getRight()const  {
		return dynamic_cast<PDTExpSet*> (children[1]);
	}
	virtual bool isUnion() {
		return true;
	}

	NodeTypeEnum getType() {
		return PDTUnion_Em;
	}
	virtual bool isBinary() {
		return true;
	}

	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		getLeft()->toString(ss);
		ss << " Union ";
		getRight()->toString(ss);
		ss << ")";
		return ss;
	}
};

class PDTIntersect: public PDTExpSet {
	PDTIntersect(PDTCodeGraph & CG, PDTExpSet & left, PDTExpSet & right) :
		PDTExpSet(CG) {
		addChild(&left);
		addChild(&right);

		left.addParent(this);
		right.addParent(this);
	}
	PDTExpSet * getLeft() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}
	PDTExpSet * getRight()const  {
		return dynamic_cast<PDTExpSet*> (children[1]);
	}
	virtual bool isIntersect() {
		return true;
	}
	NodeTypeEnum getType() {
		return PDTIntersect_Em;
	}
	virtual bool isBinary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		getLeft()->toString(ss);
		ss << " Intersect ";
		getRight()->toString(ss);
		ss << ")";
		return ss;
	}
};

class PDTComplement: public PDTExpSet {
	PDTComplement(PDTCodeGraph & CG, PDTExpSet & operand) :
		PDTExpSet(CG) {
		addChild(&operand);
		operand.addParent(this);
	}
	PDTExpSet * getOperand() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}
	virtual bool isComplement() {
		return true;
	}
	NodeTypeEnum getType() {
		return PDTComplement_Em;
	}
	virtual bool isUnary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		ss << " Complement ";
		getOperand()->toString(ss);
		ss << ")";
		return ss;
	}
};

class PDTIsMember: public PDTExpBool {
public:
	PDTIsMember(PDTCodeGraph & CG, PDTVar & var, PDTExpSet & set) :
		PDTExpBool(CG) {
		PDTNode * varNode = var.getNode();
		addChild(varNode);
		addChild(&set);
		varNode->addParent(this);
		set.addParent(this);
	}
	PDTNode * getVarNode() const {
		return children[0];
	}
	;
	PDTExpSet * getSet() const {
		return dynamic_cast<PDTExpSet*> (children[1]);
	}
	NodeTypeEnum getType() {
		return PDTMember_Em;
	}
	virtual bool isBinary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		getVarNode()->toString(ss);
		ss << " in ";
		getSet()->toString(ss);
		ss << ")";
		return ss;
	}
};

class PDTSubsetEq: public PDTExpBool {
public:
	PDTSubsetEq(PDTCodeGraph & CG, PDTExpSet & left, PDTExpSet & right) :
		PDTExpBool(CG) {
		addChild(&left);
		addChild(&right);
		left.addParent(this);
		right.addParent(this);
	}
	PDTExpSet * getLeft() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}
	PDTExpSet * getRight() const {
		return dynamic_cast<PDTExpSet*> (children[1]);
	}
	NodeTypeEnum getType() {
		return PDTSubsetEq_Em;
	}
	virtual bool isBinary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		getLeft()->toString(ss);
		ss << " SubsetEq ";
		getRight()->toString(ss);
		ss << ")";
		return ss;
	}

};

class PDTCardinality: public PDTExpNum {
public:
	PDTCardinality(PDTCodeGraph & CG, PDTExpSet & set) :
		PDTExpNum(CG) {
		addChild(&set);
		set.addParent(this);
	}
	PDTExpSet * getSet() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}

	NodeTypeEnum getType() {
		return PDTCardinality_Em;
	}
	virtual bool isUnary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << " |";
		getSet()->toString(ss);
		ss << "| ";
		return ss;
	}
};

class PDTProduct: public PDTExpSet {
public:
	PDTProduct(PDTCodeGraph & CG, PDTExpSet & left, PDTExpSet & right) :
		PDTExpSet(CG) {
		addChild(&left);
		addChild(&right);
		left.addParent(this);
		right.addParent(this);
	}
	PDTExpSet * getLeft() const {
		return dynamic_cast<PDTExpSet*> (children[0]);
	}
	PDTExpSet * getRight() const {
		return dynamic_cast<PDTExpSet*> (children[1]);
	}
	NodeTypeEnum getType() {
		return PDTProduct_Em;
	}
	virtual bool isBinary() {
		return true;
	}
	virtual stringstream & toString(stringstream & ss) const {
		ss << "(";
		getLeft()->toString(ss);
		ss << ".";
		getRight()->toString(ss);
		ss << ")";
		return ss;
	}
};
